﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using CommonFramework.Log;

namespace CommonFramework.FixMath
{
    [Serializable]
    /// <summary>
    /// 顶点数,小数精度位数固定,在帧同步中的浮点数类型不能直接使用float或double
    /// </summary>
    public struct FFloat:IEquatable<FFloat>,IEqualityComparer<FFloat>,IEqualityComparer
    {
        private int _rawVal;
        public int RawVal() => _rawVal;

        #region 精度

        public const int Precision = 1000;
        public const int HalfPrecision = Precision / 2;
        public const float PrecisionInverse = 0.001f;

        #endregion

        #region 构造

        //禁止外部直接构造
        private FFloat(int defaultVal=0)
        {
            _rawVal = defaultVal;
        }

        [Conditional("DEBUG")]
        private static void CheckOverflow(long val,bool isRow)
        {
            if (!isRow)
                val = (long) (val*PrecisionInverse);

            if (val > int.MaxValue || val < int.MinValue)
            {
                LogUtil.LogError($"FFloat overflow,with raw long value={val}");
            }
        }
        public static FFloat FromInt(int val)
        {
            return new FFloat() {_rawVal = (int) (val * PrecisionInverse)};
        }
        public static FFloat FromRawInt(int rawInt)
        {
            return new FFloat() {_rawVal = rawInt};
        }
        public static FFloat FromLong(long val)
        {
            CheckOverflow(val, false);
            return new FFloat() {_rawVal = (int) (val * PrecisionInverse)};
        }
        public static FFloat FromRawLong(long rawLong)
        {
            CheckOverflow(rawLong,true);
            return new FFloat() {_rawVal = (int) rawLong};
        }

        #endregion

        #region 常量

        public static readonly FFloat Zero = FromRawInt(0);
        public static readonly FFloat One = FromRawInt(Precision);
        public static readonly FFloat Half = FromRawInt(Precision / 2);
        public static readonly FFloat NegOne = FromRawInt(-Precision);
        public static readonly FFloat Max = FromRawInt(int.MaxValue);
        public static readonly FFloat Min = FromRawInt(int.MinValue);
        public static readonly FFloat Epslion = FromRawInt(1);

        #endregion

        #region 比较操作符

        public static bool operator <(FFloat a, FFloat b)
        {
            return a._rawVal < b._rawVal;
        }
        public static bool operator >(FFloat a, FFloat b)
        {
            return a._rawVal > b._rawVal;
        }
        public static bool operator <=(FFloat a, FFloat b)
        {
            return a._rawVal <= b._rawVal;
        }
        public static bool operator >=(FFloat a, FFloat b)
        {
            return a._rawVal >= b._rawVal;
        }
        public static bool operator ==(FFloat a, FFloat b)
        {
            return a._rawVal == b._rawVal;
        }
        public static bool operator !=(FFloat a, FFloat b)
        {
            return a._rawVal != b._rawVal;
        }

        #endregion

        #region 运算操作符

        public static FFloat operator +(FFloat a, FFloat b)
        {
            return FromRawLong((long) a._rawVal + b._rawVal);
        }
        public static FFloat operator -(FFloat a, FFloat b)
        {
            return FromRawLong(a._rawVal - b._rawVal);
        }
        public static FFloat operator *(FFloat a, FFloat b)
        {
            return FromRawLong((long) ((long)a._rawVal * b._rawVal* PrecisionInverse));
        }
        public static FFloat operator /(FFloat a, FFloat b)
        {
            return FromRawLong(((long) a._rawVal * Precision / b._rawVal));
        }
        /// <summary>
        /// 取相反数
        /// </summary>
        public static FFloat operator -(FFloat a)
        {
            return FromRawLong(-a._rawVal);
        }

        #endregion

        #region 针对int操作符重载优化

        public static FFloat operator +(FFloat a, int b)
        {
            return FFloat.FromRawLong((long)a._rawVal + b * Precision);
        }
        public static FFloat operator -(FFloat a, int b)
        {
            return FFloat.FromRawLong((long) a._rawVal - b * Precision);
        }
        public static FFloat operator *(FFloat a, int b)
        {
            return FFloat.FromRawLong((long)a._rawVal * b);
        }
        public static FFloat operator /(FFloat a, int b)
        {
            return FFloat.FromRawLong((long)a._rawVal / b);
        }

        public static FFloat operator +(int a, FFloat b)
        {
            return FFloat.FromRawLong((long)b._rawVal + a * Precision);
        }
        public static FFloat operator -(int a, FFloat b)
        {
            return FFloat.FromRawLong((long)a * Precision - b._rawVal);
        }
        public static FFloat operator *(int a, FFloat b)
        {
            return FFloat.FromRawLong((long)a * b.RawVal());
        }
        public static FFloat operator /(int a, FFloat b)
        {
            return FFloat.FromRawLong((long)a * Precision * Precision / b._rawVal);
        }
        
        public static bool operator <(FFloat a, int b)
        {
            return a._rawVal < (b * Precision);
        }
        public static bool operator >(FFloat a, int b)
        {
            return a._rawVal > (b * Precision);
        }
        public static bool operator <=(FFloat a, int b)
        {
            return a._rawVal <= (b * Precision);
        }
        public static bool operator >=(FFloat a, int b)
        {
            return a._rawVal >= (b * Precision);
        }
        public static bool operator ==(FFloat a, int b)
        {
            return a._rawVal == (b * Precision);
        }
        public static bool operator !=(FFloat a, int b)
        {
            return a._rawVal != (b * Precision);
        }

        public static bool operator <(int a, FFloat b)
        {
            return (a * Precision) < (b._rawVal);
        }
        public static bool operator >(int a, FFloat b)
        {
            return (a * Precision) > (b._rawVal);
        }
        public static bool operator <=(int a, FFloat b)
        {
            return (a * Precision) <= (b._rawVal);
        }
        public static bool operator >=(int a, FFloat b)
        {
            return (a * Precision) >= (b._rawVal);
        }
        public static bool operator ==(int a, FFloat b)
        {
            return (a * Precision) == (b._rawVal);
        }
        public static bool operator !=(int a, FFloat b)
        {
            return (a * Precision) != (b._rawVal);
        }
        #endregion

        #region 相等性接口实现

        public bool Equals(FFloat other)
        {
            if (other == null)
                return false;
            return this == other;
        }
        public bool Equals(FFloat x, FFloat y)
        {
            if(x!=null)
                return x.Equals(y);
            return y == null;
        }
        public int GetHashCode(FFloat obj)
        {
            return obj._rawVal;
        }
        public new bool Equals(object x, object y)
        {
            if (x is FFloat a && y is FFloat b)
            {
                return a == b;
            }
            else
            {
                return false;
            }
        }
        public int GetHashCode(object obj)
        {
            if (obj is FFloat a)
                return a.GetHashCode();
            else
                return obj.GetHashCode();
        }
        public override bool Equals(object obj)
        {
            if (obj == null)
                return false;
            if (obj is FFloat other)
                return this.Equals(other);
            else
                return false;
        }
        public override int GetHashCode()
        {
           return _rawVal;
        }

        #endregion
       
        public override string ToString()
        {
            return (_rawVal * PrecisionInverse).ToString(CultureInfo.CurrentCulture);
        }

        #region 数值类型转换

        public short ToShort()
        {
            return (short) (_rawVal * PrecisionInverse);
        }
        public int ToInt()
        {
            return (int) (_rawVal * PrecisionInverse);
        }
        public long ToLong()
        {
            return (long) (_rawVal * PrecisionInverse);
        }
        public float ToFloat()
        {
            return _rawVal * PrecisionInverse;
        }
        public double ToDouble()
        {
            return _rawVal * (double)PrecisionInverse;
        }

        public static implicit operator FFloat(short val)
        {
            return FromRawInt(val*Precision);
        }
        public static implicit operator FFloat(int val)
        {
            return FromRawLong((long)val*Precision);
        }
        public static implicit operator FFloat(long val)
        {
            return FromRawLong(val * Precision);
        }
        public static explicit operator FFloat(float val)
        {
            return FromRawLong((long) (val * Precision));
        }
        public static explicit operator FFloat(double val)
        {
            return FromRawLong((long) (val * Precision));
        }

        #endregion
    }
}